/***************************************************************************
 *   Copyright (C) 2009 by Marian Skvarek   *
 *   skvarek.marian@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A TrackICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "CBar.h"
#include "CBarView.h"
#include "CSymbol.h"
#include "CSymbolView.h"
#include "CNoteGroup.h"
#include "CPause.h"
#include "CDebug.h"
#include "CEasyScoreApp.h"

using namespace easyscore::utils;

namespace easyscore
{
    namespace core
    {
        CBar::CBar ( CTrack *parentTrack )
        {
            mParentTrack = parentTrack;
            mActive = true;
            mTempNoteGroup = 0;
            mView = 0;

            initialize();
        }

        void CBar::initialize()
        {
            //insert pause
            CFraction timeSignature = getParentScore()->getTime();
            appendRests ( ( timeSignature.getNumerator() * NOTE_VALUE_WHOLE ) / timeSignature.getDenominator(), NOTE_VALUE_LONGA, mSymbols ) ;
        }

        void CBar::clear()
        {
            clearSymbols();

            destroyView();
        }

        CBar::~CBar()
        {
            clear();
        }

        void CBar::insertSymbol ( CSymbol *symbol, CSymbol *before )
        {
            if ( before != 0 )
            {
                QLinkedList<CSymbol *>::iterator instead = getIterator ( before );
                mSymbols.insert ( instead,symbol );
            }
            else
            {
                mSymbols.append ( symbol );
            }

            symbol->setParentBar ( this );

            updateView();
        }

        void CBar::removeSymbol ( CSymbol *symbol )
        {
            if ( !mSymbols.contains ( symbol ) )
                return;

            mSymbols.removeOne ( symbol );
            delete symbol;

            updateView();
        }

        void CBar::replaceSymbol(CSymbol *symbol, CSymbol *replacement)
        {
            suspendView();

            insertSymbol(replacement, symbol);
            removeSymbol(symbol);

            resumeView();
            updateView();
        }

        void CBar::insertTemporaryNoteGroup ( CPause *underlyingPause )
        {
            suspendView();

            removeTemporaryNoteGroup();

            mTempNoteGroup = new CNoteGroup();
            mTempNoteGroup->setValue ( underlyingPause->getValue() );
            mTempNoteGroup->setParentBar ( this );
            mTempNoteGroup->initialize();

            replaceSymbol ( underlyingPause, mTempNoteGroup );

            resumeView();
            updateView();
        }

        void CBar::removeTemporaryNoteGroup()
        {
            if ( mTempNoteGroup)
            {
                removeNoteGroup(mTempNoteGroup);
                mTempNoteGroup = 0;
            }
        }

        void CBar::removeNoteGroup(CNoteGroup *noteGroupToRemove)
        {
            suspendView();

            replaceSymbol ( noteGroupToRemove, createPause ( noteGroupToRemove->getValue() ) );
            mTempNoteGroup = 0;

            resumeView();
            updateView();
        }

        void CBar::confirmTemporaryNoteGroup()
        {
            mTempNoteGroup = 0;

            getParentScore()->setModified(true);
        }

        void CBar::unionRests()
        {
            splitRests ( NOTE_VALUE_LONGA );
        }

        void CBar::splitRests()
        {
            splitRests ( getParentScore()->getParentApp()->getCurrentNoteValue() );
        }

        void CBar::splitRests ( NOTE_VALUE currentValue )
        {
           // CDebug::getInstance()->writeDebugInfo("\tSplitting rests...");

            QLinkedList<CSymbol *> newSymbols;

            NOTE_VALUE pauseValue = 0;
            foreach ( CSymbol *symbol, mSymbols )
            {
                if ( !symbol->isRest() )
                {
                    appendRests ( pauseValue,currentValue,newSymbols );
                    pauseValue = 0;

                    newSymbols.append ( symbol );
                }
                else
                {
                    pauseValue += symbol->getValue();
                    //temp - causes segfault
                    //delete symbol;
                    symbol->getSymbolView()->setVisible(false);
                }


            }

            appendRests ( pauseValue,currentValue, newSymbols );

            mSymbols = newSymbols;

            updateView();

            CDebug::getInstance()->writeDebugInfo("\tRests splitted");
        }

        /*!
		\fn easyscore::core::CBar::createView()
		\brief creates a view
		*/
        void CBar::createView()
        {
            destroyView();

            mView = new CBarView ( this );
        }

        /*!
		\fn easyscore::core::CBar::destroyView()
		\brief destroys a view
		 */
        void CBar::destroyView()
        {
            if ( mView != 0 )
            {
                delete mView;
                mView = 0;
            }
        }

        void CBar::suspendView()
        {
            if ( hasView() )
                mView->setSuspended ( true );
        }

        void CBar::resumeView()
        {
            if ( hasView() )
                mView->setSuspended ( false );
        }

        //Private methods
        CPause *CBar::createPause ( NOTE_VALUE value )
        {
            CPause *newPause = new CPause ( value );
            newPause->setParentBar ( this );
            newPause->initialize();
            return newPause;
        }

        void CBar::clearSymbols()
        {
            foreach ( CSymbol *symbol, mSymbols )
            {
                delete symbol;
            }
            mSymbols.clear();
        }

        QLinkedList<CSymbol *>::iterator CBar::getIterator ( CSymbol *symbol )
        {
            for ( QLinkedList<CSymbol *>::iterator i = mSymbols.begin();i != mSymbols.end(); i++ )
            {
                if ( *i == symbol )
                    return i;
            }

            //CDebug::getInstance()->writeDebugInfo ( "Iterator not found ( in CBar )" );
            return 0;
        }

        void CBar::appendRests ( NOTE_VALUE totalValue, NOTE_VALUE maxPauseValue, QLinkedList<CSymbol *> &symbolList )
        {
            while ( totalValue )
            {
                NOTE_VALUE minValue = easyscore::utils::CUtils::min ( totalValue, maxPauseValue );
                NOTE_VALUE minValueCopy = minValue;

                NOTE_VALUE currentValue = NOTE_VALUE_LONGA;
                while ( minValue )
                {
                    if ( currentValue <= minValue )
                    {
                        CPause *p = new CPause ( currentValue );
                        p->setParentBar ( this );
                        p->initialize();
                        symbolList.append ( p );

                        minValue -= currentValue;
                    }
                    else
                    {
                        currentValue /= 2;
                    }
                }


                totalValue -= minValueCopy;
            }
        }

        void CBar::updateView()
        {
            if ( hasView() )
                mView->update();
        }


    }//namespace core
}//namespace easyscore
